import EventEmitter from "events";
import SymbolOrderConfigEntity, { SymbolOrderMoreConfig } from "../module/symbol-order-config/SymbolOrderConfig.entity";
import { OrderWatch, Order } from "./OrderWatch";
import { RiskModel } from "./RiskModel";
import { outLogger } from "../common/logger";
import AutoOrderHistoryEntity from "../module/order-history/AutoOrderHistory.entity";
import { AutoOrderConfig, AutoTradeType } from "./types";
import { OrderSide, OrderStatus, PositionSide } from "binance";


export class ExchangeTraderBase {
  private readonly exchange: string;
  private readonly userid: number;
  private readonly username: string;
  public orderWatch: OrderWatch;
  public riskModel: RiskModel;
  private eventEmitter = new EventEmitter();

  static getInitialMargin = (price: number, leverage: number, size: number) => {
    return size * price  / leverage;
  }
  // 维持保证金 = 仓位名义价值 * 维持保证金率 - 维持保证金速算额
  static maintenanceMargin =  (price: number, cum: number, size: number, maintenanceMarginRate: number) => {
    return size * price * maintenanceMarginRate - cum;
  }
  
  constructor(exchange: string, userid: number, opitons: {
    username?: string
  }) {
    this.exchange = exchange;
    this.userid = userid;
    this.username = opitons.username;
    this.orderWatch = new OrderWatch();
    this.riskModel = new RiskModel();
  }

  getUserId() {
    return this.userid;
  }

  getInfo() {
    return {
      exchange: this.exchange,
      userid: this.userid,
      username: this.username
    };
  }
  updateFuturesPrice(symbol: string, close: number) {
    this.riskModel.updatePositionsPrice(symbol,'futures', close);
    this.orderWatch.updatePrice(symbol, 'futures' , close);
  }
  updateDeliveryPrice(symbol: string, close: number) {
    this.riskModel.updatePositionsPrice(symbol,'delivery', close);
    this.orderWatch.updatePrice(symbol, 'delivery' , close);
  }
  updateRiskModelConfig(symbol: string, config: SymbolOrderMoreConfig) {
    this.riskModel.updateConfig(symbol, config);
    outLogger.info(`userid: ${this.userid} updateRiskModelConfig`, symbol, config);
  }

  watchOrder(orderHistory: (AutoOrderHistoryEntity)[]) {
    let userid = 0
    const symbolMap = orderHistory.reduce((pre, cur) => {
      pre[cur.symbol] = cur;
      return pre;
    }, {} as Record<string, AutoOrderHistoryEntity>);

    // 清空历史
    Object.keys(symbolMap).forEach(symbol => {
      this.orderWatch.clear(symbol, symbolMap[symbol].trade_type)
    });
    let count = 0;
    orderHistory.forEach((item, index) => {
      userid = item.userid
      const clientOrderId = (item as AutoOrderHistoryEntity).order_id;
      if (item.status !== 'NEW' && item.status !== 'PARTIALLY_FILLED') {
        this.orderWatch.removeOrder({symbol: item.symbol, clientOrderId: clientOrderId, tradeType: item.trade_type});
        return
      }

      const [side, positionSide] = item.action.split('-');
      this.orderWatch.addOrder({
        clientOrderId: item.order_id,
        price: item.price,
        side: side as any,
        positionSide: positionSide as any,
        status: item.status as any,
        symbol: item.symbol,
        tradeType: item.trade_type as any,
      });
      count++;
    });
    outLogger.info(`userid: ${userid} watchOrder: success: ${count}, sum: ${orderHistory.length}`);
  }
   
  orderAfter(params: {
    symbol: string;
    price: number | string;
    quantity: number | string;
    action: string; // BUY-SELL
    tradeType: AutoTradeType;
    orderType: AutoOrderConfig['order_type'];
    orderId?: number | string;
    status: string
    raw?: Record<string, any>
  }) {
    if (!params.orderId) {
      return;
    }
    const [side, positionSide] = params.action.split('-');
    if (params.orderType === 'LIMIT' || (params.orderType === 'MARKET' && (params.action === 'BUY-LONG' || params.action === 'SELL-SHORT'))) {
      this.orderWatch.addOrder({
        clientOrderId: params.orderId,
        orderId: params.orderId,
        price: params.price,
        side: side as OrderSide,
        positionSide: positionSide as PositionSide,
        status: params.status as OrderStatus,
        symbol: params.symbol,
        tradeType: params.tradeType,
      });
    }
    this.notificationOrder(params);
  }

  onOrderNotification(callback: (data: {
    symbol: string;
    price: number | string;
    quantity: number | string;
    action: "BUY" | "SELL"; // BUY-SELL SELL-SHORT
    tradeType: AutoTradeType;
    orderType: AutoOrderConfig['trade_type'];
    orderId?: number | string;
    status: string
    userid: number;
    raw?: Record<string, any>
  }) => void) {
    this.eventEmitter.on('order', callback);
  }

  notificationOrder(params: {
    symbol: string;
    price: number | string;
    quantity: number | string;
    action: string; // BUY-SELL
    tradeType: AutoTradeType;
    orderType: AutoOrderConfig['order_type'];
    orderId?: number | string;
    status: string
    raw?: Record<string, any>
  }) {
    this.eventEmitter.emit('order', Object.assign(params, {userid: this.getUserId(), username: this.getInfo().username}));
  }
  destroy() {
    this.eventEmitter.removeAllListeners();
    this.orderWatch.distory();
    this.riskModel.destroy();
    this.eventEmitter = null;
  }
}